/* * This file is part of ZSE Info. * * ZSE Info is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * any later version. * * ZSE Info is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.enbyted.android.zseinfo.data.replacements; import android.util.Log; import org.enbyted.android.zseinfo.data.configuration.Configuration; import org.htmlcleaner.HtmlCleaner; import org.htmlcleaner.TagNode; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; /** * Created by Bartosz Grabias on 23.02.14. */ public class VulcanReplacementsHandler extends ReplacementsHandler { public static final String TAG = "ZSEINFO_VULCAN_REPLACEMENTS"; @Override public Replacements loadReplacements() throws IOException { TagNode node = downloadReplacements(); if(node.findElementByName("head", true) != null) { return processXHTML(node); } else { return processHTML(node); } } // final int CELL_TEACHER = 0; // final int CELL_TITLE = 1; // final int CELL_HEADER = 2; // final int CELL_LESSON = 3; // final int CELL_DESCRIPTION = 4; // final int CELL_REPLACEMENT = 5; // final int CELL_REMARKS = 6; private enum CellType { NONE, TITLE, TEACHER, HEADER, LESSON, DESCRIPTION, REPLACEMENT, REMARKS, SEPARATOR, } private static TagNode downloadReplacements() throws IOException { HtmlCleaner cleaner = new HtmlCleaner(); String url = Configuration.getInstance().getReplacementsConfig().getReplacementsUrl(); InputStream input; HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); if(connection.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new IOException("Błąd połączenia z serwerem: " + connection.getResponseCode() + " " + connection.getResponseMessage()); } input = connection.getInputStream(); TagNode node = cleaner.clean(input, Configuration.getInstance().getReplacementsConfig().getEncoding()); try { return node.findElementByName("table", true) .findElementByName("tbody", false); } catch (NullPointerException e) { if(node.findElementByName("frame", true) != null) { TagNode frame = node.findElementByName("frame", true); String sheetAddr = frame.getAttributeByName("src"); connection = (HttpURLConnection) new URL(url + sheetAddr).openConnection(); if(connection.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new IOException("Błąd połączenia z serwerem: " + connection.getResponseCode() + " " + connection.getResponseMessage()); } input = connection.getInputStream(); node = cleaner.clean(input, Configuration.getInstance().getReplacementsConfig().getEncoding()); return node;//.findElementByName("table", true) //.findElementByName("tbody", false); } throw e; } } private Replacements processXHTML(TagNode table) { Log.v(TAG, "Starting replacements XHTML processing"); //Due to some weird bug all TDs are in root table element TagNode[] cells = table.getElementsByName("td", true); final String classTeacherName = "xl69"; final String classTitle = "xl68"; List<Replacement> replacements = new ArrayList<>(); List<Replacement.Entry> entries = new ArrayList<>(); String title = ""; String teacher = ""; int lessonId = 0; String description = ""; String replacement = ""; String remarks = ""; int headerCount = 0; boolean replacementAdded = false; CellType cellType = CellType.NONE; for(TagNode cell : cells) { // Cell type detection here String cellClass = cell.getAttributeByName("class"); if(cellClass == null) cellClass = ""; Log.d(TAG, "Cell with class: " + cellClass); if(cellType == CellType.SEPARATOR && ! replacementAdded) { Log.d(TAG, "Adding replacement"); replacementAdded = true; String[] dateParts = title.split(" ", 4); replacements.add(new Replacement( teacher, entries.toArray(new Replacement.Entry[entries.size()]), dateParts.length==4?dateParts[3]:title )); } else if(cellType != CellType.SEPARATOR) { replacementAdded = false; } switch(cellClass) { case classTeacherName: { Log.v(TAG, "Looks like we hit a teacher."); cellType = CellType.TEACHER; break; } case classTitle: { Log.v(TAG, "Looks like we hit a title."); cellType = CellType.TITLE; break; } default: { switch(cellType) { case TEACHER: { Log.v(TAG, "Looks like we hit a header."); cellType = CellType.HEADER; break; } case HEADER: { headerCount++; if(headerCount < 3) break; headerCount = 0; //fallthrough } case SEPARATOR: case REMARKS: { Log.v(TAG, "Looks like we hit a lesson."); cellType = CellType.LESSON; break; } case LESSON: { Log.v(TAG, "Looks like we hit a description."); cellType = CellType.DESCRIPTION; break; } case DESCRIPTION: { Log.v(TAG, "Looks like we hit a replacement."); cellType = CellType.REPLACEMENT; break; } case REPLACEMENT: { Log.v(TAG, "Looks like we hit a remarks."); cellType = CellType.REMARKS; break; } default: { Log.w(TAG, "PANIC! HTML processor is in unknown state, I can't detect cell type. Current cell type is: " + cellType.toString()); Log.w(TAG, "Assuming it's a header row, but THIS IS BUG!"); cellType = CellType.HEADER; break; } } break; } } //Cell data processing here switch(cellType) { case TITLE: { title = clearString(cell.getText()); break; } case TEACHER: { teacher = clearString(cell.getText()); entries.clear(); break; } case LESSON: { lessonId = 0; description = ""; replacement = ""; remarks = ""; try { lessonId = Integer.valueOf(clearString(cell.getText())); } catch (Exception e) { //If we can't parse lesson ID then it is a separator row OR something is wrong with parser. Log.v(TAG, "Looks like we hit a separator.", e); cellType = CellType.SEPARATOR; continue; } break; } case DESCRIPTION: { description = clearString(cell.getText()); break; } case REPLACEMENT: { replacement = clearString(cell.getText()); break; } case REMARKS: { remarks = clearString(cell.getText()); Log.d(TAG, "Adding new entry with ID="+lessonId+", desc="+description+", repl="+replacement+"rem="+remarks); entries.add(new Replacement.Entry( lessonId, description, replacement, remarks )); break; } } } return new Replacements(title, replacements.toArray(new Replacement[replacements.size()])); } private String clearString(CharSequence input) { String val = input.toString().trim(); return val.equals(" ")?"":val; } private Replacements processHTML(TagNode table) { Log.v(TAG, "Starting replacements HTML processing"); TagNode[] rows = table.getAllElements(false); final String classTitle = "st0"; final String classTeacher = "st1"; List<Replacement> replacements = new ArrayList<>(); List<Replacement.Entry> entries = new ArrayList<>(); String title = ""; String teacher = ""; int lessonId = 0; String description = ""; String replacement = ""; String remarks = ""; boolean firstRow = false; boolean replacementAdded = false; CellType cellType = CellType.NONE; for(TagNode rowNode : rows) { if(! rowNode.getName().equalsIgnoreCase("tr")) continue; if(cellType == CellType.SEPARATOR && !replacementAdded) { String[] dateParts = title.split(" ", 4); replacements.add(new Replacement( teacher, entries.toArray(new Replacement.Entry[entries.size()]), dateParts.length==4?dateParts[3]:title )); entries.clear(); lessonId = 0; description = ""; replacement = ""; remarks = ""; replacementAdded = true; } else if(cellType != CellType.SEPARATOR) { replacementAdded = false; } TagNode[] cells = rowNode.getAllElements(false); for(TagNode cellNode : cells) { if(! cellNode.getName().equalsIgnoreCase("td")) continue; String cellClass = cellNode.getAttributeByName("class"); Log.d(TAG, "Cell with class: " + cellClass); switch(cellClass) { case classTitle: { Log.v(TAG, "Looks like a title cell"); cellType = CellType.TITLE; break; } case classTeacher: { Log.v(TAG, "Looks like a teacher cell"); cellType = CellType.TEACHER; break; } default: { switch(cellType) { case SEPARATOR: { Log.v(TAG, "Separator, skipping"); continue; } case TEACHER: { Log.v(TAG, "Looks like a header cell"); cellType = CellType.HEADER; break; } case REMARKS: case HEADER: { Log.v(TAG, "Looks like a lesson cell"); cellType = CellType.LESSON; break; } case LESSON: { Log.v(TAG, "Looks like a description cell"); cellType = CellType.DESCRIPTION; break; } case DESCRIPTION: { Log.v(TAG, "Looks like a replacement cell"); cellType = CellType.REPLACEMENT; break; } case REPLACEMENT: { Log.v(TAG, "Looks like a remarks cell"); cellType = CellType.REMARKS; break; } default: { Log.w(TAG, "PANIC! HTML processor is in unknown state, I can't detect cell type. Current cell type is: " + cellType.toString()); Log.w(TAG, "Assuming it's a header row, but THIS IS BUG!"); cellType = CellType.HEADER; break; } } break; } } switch (cellType) { case TITLE: { title = clearString(cellNode.getText()); break; } case TEACHER: { teacher = clearString(cellNode.getText()); break; } case LESSON: { try { lessonId = Integer.valueOf(clearString(cellNode.getText())); } catch (Exception e) { //If we can't parse lesson ID then it is a separator row OR something is wrong with parser. Log.v(TAG, "Looks like we hit a separator.", e); cellType = CellType.SEPARATOR; continue; } break; } case DESCRIPTION: { description = clearString(cellNode.getText()); break; } case REPLACEMENT: { replacement = clearString(cellNode.getText()); break; } case REMARKS: { remarks = clearString(cellNode.getText()); Log.d(TAG, "Adding new entry with ID="+lessonId+", desc="+description+", repl="+replacement+"rem="+remarks); entries.add(new Replacement.Entry( lessonId, description, replacement, remarks )); break; } } if( cellType == CellType.SEPARATOR || cellType == CellType.HEADER ) break; } } if(entries.size() > 0) { String[] dateParts = title.split(" ", 4); replacements.add(new Replacement( teacher, entries.toArray(new Replacement.Entry[entries.size()]), dateParts.length==4?dateParts[3]:title )); } return new Replacements(title, replacements.toArray(new Replacement[replacements.size()])); } @Override public String getType() { return "vulcan"; } }